<?php
global $CFG;
 
require_once($CFG->libdir. '/simpletestlib/unit_tester.php');
require_once($CFG->libdir. '/simpletestlib/mock_objects.php');
require_once($CFG->libdir. '/simpletestlib.php');

require_once(dirname(__FILE__).'/../module_resourcepage.php');
require_once(dirname(__FILE__).'/../resourcepage_extension_push.php');

class module_resourcepage_test extends UnitTestCase {
    
    private $module;
    private $beforeprefix;
    
    const TESTPREFIX='unittest_';
    const TESTPREFIXORA='t_';
    
    private function getTestPrefix() {
        global $CFG;
        if (isset($CFG->dbfamily) && ($CFG->dbfamily == 'oracle')) {
            return self::TESTPREFIXORA;
        }
        return self::TESTPREFIX;
    }
    
    function setUp() {
        global $CFG;

        // Set $CFG prefix for testing
        $this->beforeprefix=$CFG->prefix;
        $CFG->prefix=$this->getTestPrefix();

        $this->module=new module_resourcepage();
        $this->delete_tables();
        $this->create_other_tables();

        // Hopefully temp but for now Oracle takes more than 2 minutes so allow 5
        if (isset($CFG->dbfamily) && ($CFG->dbfamily == 'oracle')) {
            set_time_limit(600);
        }
    }
    
    function create_other_tables() {
        global $CFG;        
        $sql=<<<QUERYENDS
CREATE TABLE mdl_cache_text
(
  id integer NOT NULL,
  md5key varchar(32) NOT NULL,
  formattedtext varchar(4000),
  timemodified integer DEFAULT 0 NOT NULL,
  CONSTRAINT mdl_cache_text_pkey PRIMARY KEY (id)
)
QUERYENDS;
        $sql=preg_replace('/mdl_/',$CFG->prefix,$sql);
        if(!execute_sql($sql, false)) {
            throw new Exception("Error creating temp Moodle tables");
        } 

        $sql=<<<QUERYENDS
CREATE INDEX mdl_cache_text_md5key_idx
  ON mdl_cache_text
  (md5key)    
QUERYENDS;
        $sql=preg_replace('/mdl_/',$CFG->prefix,$sql);
        if(!execute_sql($sql, false)) {
            throw new Exception("Error creating temp Moodle tables");
        } 
    }
    
    function delete_tables() {
        global $db;
        wipe_tables($this->getTestPrefix(), $db);
        wipe_sequences($this->getTestPrefix(), $db);
    }
    
    function tearDown() {
        $this->delete_tables();
        // Put prefix back
        global $CFG;
        $CFG->prefix=$this->beforeprefix;
    }
    
    function oldtest_everything() {        
        global $db;
        $this->module->install();
        
        $tables=$db->Metatables();
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_placeholders',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_flags',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_items',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_files',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_itemflags',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_containers',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_containeritems',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_canned',$tables));
        $this->assertTrue(in_array($this->getTestPrefix().'resourcepage_cannedusage',$tables));

        $tw=new transaction_wrapper();
        //$db->debug=true;
        $this->module->add_flag('jollyroger');
        $this->module->add_flag('bluepeter');
        $this->module->add_flag('redensign');
        $this->module->add_placeholder('coaster');
        
        $item=$this->module->add_item(
            'weblink',true,
            null,null,null,
            'Google (1)','A <strong>search</strong> engine',
            null,
            'http://www.google.com/',null,null);
        $this->module->add_item_flag($item,'jollyroger');
        $this->module->add_item_flag($item,'redensign');
        $this->module->add_item_file($item,'somewhere/i.suck.txt','text/plain',666);
        $this->module->add_item_file($item,'somewhere/i.suck.more.doc','application/msword',666666);
        
        $alias=$this->module->add_item(
            null,true,
            null,null,null,
            'Changed (6)',null,
            null,
            null,null,$item);
        $this->module->add_item_flag($alias,'bluepeter',false);
        
        
        
        $i1=$this->module->add_item('weblink',true,
            null,null,null,
            'Another link (2)','More junk',
            null,
            'http://www.elsewhere.com/',null,null);        
        $i2=$this->module->add_item('weblink',true,
            null,null,null,
            'Another link (3)','More junk',
            null,
            'http://www.elsewhere.com/',null,null);        
        $i3=$this->module->add_item('weblink',true,
            null,null,null,
            'Yet another link (4)','More junk',
            null,
            'http://www.elsewhere.com/',null,null);        
        $i4=$this->module->add_item('weblink',true,
            null,null,null,
            'One more bloody link (5)','More junk',
            null,
            'http://www.elsewhere.com/',null,null);        
            
        $cont=$this->module->add_container('_page');
        $innercont=$this->module->add_container('_section');
        $innerinnercont=$this->module->add_container('_section');
        $recycle=$this->module->add_container('_deleted');
        
        $this->module->add_container_item($cont,$item); 
        $this->module->add_container_subcontainer($cont,$innercont);       
        $this->module->add_container_item($cont,$alias);        

        $this->module->add_container_item($innercont,$i1);        
        $this->module->add_container_item($innercont,$i2);        
        $this->module->add_container_subcontainer($innercont,$innerinnercont);
        $this->module->add_container_item($innercont,$i4);        
        
        //$this->module->add_container_item($innerinnercont,$i3);        
        
        $tw->commit();
        
        $extract=$this->module->get_container_items($cont);
//        print "<pre>";
//        print_r($extract);
//        print '</pre>';


        // Course started 10,000 seconds ago
        $course->startdate=time()-10000;
        $extract[0]->display($course);
    }
    
    private function assert_sql_count($table,$condition,$count) {
        global $db;
        $where = $condition == 'TRUE' ? '' : ' WHERE '.$condition;
        $rs=$db->Execute("SELECT COUNT(*) AS cnt FROM ".$this->getTestPrefix()."resourcepage_$table".$where);
        if(!$rs) throw new Exception('DB error: '.$db->ErrorMsg());
        $this->assertEqual($rs->fields['cnt'],$count);        
    }
    
    private function assert_sql_data($query,$array) {
        global $db;
        $rs=$db->Execute(preg_replace('/modprefix_/',$this->getTestPrefix().'resourcepage_',$query));
        if(!$rs) throw new Exception('DB error: '.$db->ErrorMsg());
        $output=array();
        while(!$rs->EOF) {
            $unsorted=array_values($rs->fields);
            $output[]=$unsorted[0];
            $rs->MoveNext();
        }
        $this->assertEqual($array,$output);
    }
    
    public function test_delete() {
        $this->fill_tables();
        
        $this->module->delete_item(6);
        $this->assert_sql_count('items','id=6',0);
        $this->assert_sql_count('containeritems','containerid=1',3);
        $this->assert_sql_data(
            'SELECT itemid FROM modprefix_containeritems WHERE containerid=1 ORDER BY sequence',
            array(5,7,8));
        $this->check_sequence(1);

        // Hack so that it'll let us delete the container
        global $db;
        $this->module->do_sql('UPDATE prefix_resourcepage SET containerid=2 WHERE containerid=1');        
            
        $this->module->delete_container_with_items(1);
        $this->assert_sql_count('items','id>0',5);
        $this->assert_sql_count('containers','id>0',2);
        $this->assert_sql_count('containeritems','containerid=1',0);
        $this->assert_sql_count('containeritems','containerid=2',4);
        $this->assert_sql_data(
            'SELECT itemid FROM modprefix_containeritems WHERE containerid=2 ORDER BY sequence',
            array(1,2,3,4));
        $this->check_sequence(2);
    }
    
    public function test_evilquery() {
        $this->fill_tables();
        
        $output=$this->module->get_container_items(1,3);
        $this->assertEqual(count($output),2);
        
        $this->assertEqual($output[0]->get_child_count(),4);
        $this->assertEqual($output[1]->get_child_count(),0);        
    }
    
    private function check_sequence($container) {
        global $db;
        $rs=$db->Execute(
            'SELECT sequence FROM '.$this->getTestPrefix().'resourcepage_containeritems '.
            'WHERE containerid='.$container.' ORDER BY sequence');
        if(!$rs) throw new Exception('DB error: '.$db->ErrorMsg());
        $expected=1; $fail=false;
        while(!$rs->EOF) {
            if($rs->fields['sequence']!=$expected) {
                $fail=true;
                break;
            }
            $expected++;
            $rs->MoveNext();
        }
        $this->assertTrue(!$fail);
    }
    
    public function test_insert() {
        $this->fill_tables();
        
        // Insert at beginning
        $this->module->insert_container_item(1,1,1);
        $this->check_sequence(1);
        $this->check_sequence(2);
        // At end
        $this->module->insert_container_item(1,2,6);
        $this->check_sequence(1);
        // In middle
        $this->module->insert_container_item(2,8,3);
        $this->check_sequence(2);        
    }
    
    private function fill_tables() {
        
        global $db;
        $tw=new transaction_wrapper();
        $this->install();// derekw new testing //
                
        // Some items
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title1','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title2','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title3','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title4','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title5','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title6','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title7','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title8','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        $this->insert_row('items',"DEFAULT,'weblink',1,NULL,NULL,NULL,'Title9','',NULL,NULL,'http://',NULL,NULL,NULL,NULL,NULL,NULL");
        // Add main table row (getting round circular dependencies)
        $this->insert_row(null,"DEFAULT,1,'testpage',NULL,NULL,0,NULL,0,NULL");

        // Add another main table row for container 3
        // so it is not deleted when resourcepage 1 is deleted
        $this->insert_row(null,"DEFAULT,1,'testpage2',NULL,NULL,0,NULL,0,NULL");
        
        // Some containers 
        $this->insert_row('containers',"DEFAULT,'test',NULL,NULL,NULL,1,NULL,NULL,NULL"); // 1
        $this->insert_row('containers',"DEFAULT,'test',NULL,NULL,NULL,1,NULL,NULL,NULL"); // 2
        $this->insert_row('containers',"DEFAULT,'test',NULL,NULL,NULL,2,NULL,NULL,NULL"); // 3
        
        // Update main table row (getting round circular dependencies)
        $rs=$db->Execute('UPDATE '.$this->getTestPrefix().'resourcepage '.
                         'SET containerid=1, deletedcontainerid=2 '.
                         "WHERE course=1 AND name='testpage'");

        // Insert items 5-8 into container 1
        $this->insert_row('containeritems',"1,1,5,NULL,0"); 
        $this->insert_row('containeritems',"1,2,6,NULL,0");
        $this->insert_row('containeritems',"1,3,7,NULL,0");
        $this->insert_row('containeritems',"1,4,8,NULL,0");
        
        // Insert items 1-3 into container 2
        $this->insert_row('containeritems',"2,1,1,NULL,0"); 
        $this->insert_row('containeritems',"2,2,2,NULL,0");
        $this->insert_row('containeritems',"2,3,3,NULL,0");
        
        // Insert container 1 into container 2
        $this->insert_row('containeritems',"2,4,NULL,1,0");

        // And item 4
        $this->insert_row('containeritems',"2,5,4,NULL,0"); 
        
        $tw->commit();        
    }
    
    private function install() {
        global $CFG;        

        $mod = 'resourcepage';
        $fullmod = $CFG->dirroot .'/mod/'. $mod;
        $status = false;
        if (file_exists($fullmod . '/db/install.xml')) {
            ob_start();
            $status = install_from_xmldb_file($fullmod . '/db/install.xml');
            $this->module->create_extensions_list();
            ob_end_clean();
        }
        $this->assertTrue($status, 'Resourcepage module installation');
    }

    private function insert_row($table,$items) {
        global $db;
        $undertable=$table ? '_'.$table : '';
        if(!$db->Execute('INSERT INTO '.$this->getTestPrefix().'resourcepage'.$undertable.' VALUES ('.$items.')')) {
            throw new Exception('wtf? '.$db->ErrorMsg());
        }
    }
    
    function test_delete_instance() {
        $this->fill_tables();
        $this->module->delete_instance(1);
        // Everything in a container should've gone 
        $this->assert_sql_count('containeritems','TRUE',0);
        // Check only item 9 is left
        $this->assert_sql_count('items','TRUE',1);
        $this->assert_sql_count('items','id=9',1);
        // Only container 3 wasn't involved
        $this->assert_sql_count('containers','TRUE',1); 
        $this->assert_sql_count('containers','id=3',1);         
    }

    
    function test_get_canned_info() {
        $this->fill_tables();
        
        // Add canned stuff
        $this->insert_row('canned',"DEFAULT,'a','html1','display1'");
        $this->insert_row('canned',"DEFAULT,'b','html2','display2'");
        $this->insert_row('canned',"DEFAULT,'c','html3','display3'");
        $this->insert_row('canned',"DEFAULT,'d','html4','display4'");
        $this->insert_row('canned',"DEFAULT,'e','html5','display5'");
        
        // Get general list
        $result=print_r($this->module->get_canned_info(),true);
        // Uncomment this line to save the contents when updating the test
        //file_put_contents(dirname(__FILE__).'/../../../../moodledata/canned1.testcase',$result);
        $storedresult=file_get_contents(dirname(__FILE__).'/canned1.testcase');        
        $this->assertEqual($result,$storedresult);
        
        // Get list for resourcepage 1 (should be same to begin with)
        $result=print_r($this->module->get_canned_info(1),true);
        $storedresult=file_get_contents(dirname(__FILE__).'/canned1.testcase');        
        $this->assertEqual($result,$storedresult);
        
        // Add some usages into resourcepage1
        $this->insert_row('cannedusage',"1,'c',0,1"); // Item 3 at top
        $this->insert_row('cannedusage',"1,'b',0,2"); // Item 2 just below
        $this->insert_row('cannedusage',"1,'a',1,1"); // Item 1 at bottom
        
        $result=print_r($this->module->get_canned_info(1),true);
        // Uncomment this line to save the contents when updating the test
        //file_put_contents(dirname(__FILE__).'/../../../../moodledata/canned2.testcase',$result);
        $storedresult=file_get_contents(dirname(__FILE__).'/canned2.testcase');        
        $this->assertEqual($result,$storedresult);
    }
    
    function test_add_placeholder_item() {
        global $db;
        make_test_tables_like_real_one(array(
            'resourcepage_items','resourcepage_containeritems','resourcepage_placeholderitms'),
            $this->beforeprefix,$this->getTestPrefix(),$db,true);
        load_test_data($this->getTestPrefix().'resourcepage_items',array(
            array('id'),
            array(1),
            array(2),
            array(3),
            array(4),
            array(5)
            ),$db);
        load_test_data($this->getTestPrefix().'resourcepage_containeritems',array(
            array('containerid','itemid','sequence'),
            array(1,1,1), // Begin with item 1
            array(1,2,2), // Then item 2 which we'll treat as placeholder
            ),$db);
            
        // 2 successful attempts
        $this->module->add_placeholder_item(2,3);        
        $this->assert_sql_data(
            'SELECT itemid FROM modprefix_containeritems WHERE containerid=1 ORDER BY sequence',
            array(1,6,2));
        $this->assert_sql_data(
            'SELECT placeholderitemid FROM modprefix_placeholderitms ORDER BY placeholderitemid',
            array(2));
        $this->assert_sql_data(
            'SELECT baseitemid FROM modprefix_placeholderitms ORDER BY baseitemid',
            array(3));
        $this->assert_sql_data(
            'SELECT basedonitemid FROM modprefix_items WHERE id=6',array(3));
            
        $this->module->add_placeholder_item(2,4);        
        $this->assert_sql_data(
            'SELECT itemid FROM modprefix_containeritems WHERE containerid=1 ORDER BY sequence',
            array(1,6,7,2));
        $this->assert_sql_data(
            'SELECT placeholderitemid FROM modprefix_placeholderitms ORDER BY placeholderitemid',
            array(2,2));
        $this->assert_sql_data(
            'SELECT baseitemid FROM modprefix_placeholderitms ORDER BY baseitemid',
            array(3,4));
        $this->assert_sql_data(
            'SELECT basedonitemid FROM modprefix_items WHERE id=7',array(4));
        
        // Placeholder that doesn't exist
        try {    
            $this->module->add_placeholder_item(10,5);
            $this->assertTrue(false);
        } catch(Exception $e) {
            $this->assertTrue(true);
        }        
    }
    
    function test_get_placeholder_added_items() {
        global $db;
        make_test_tables_like_real_one(array(
            'resourcepage_items','resourcepage_placeholderitms'),
            $this->beforeprefix,$this->getTestPrefix(),$db,true);
        load_test_data($this->getTestPrefix().'resourcepage_items',array(
            array('id','title'),
            array(1,'test 1'),
            array(2,'test 2'),
            array(3,'test 3')
            ),$db);
        load_test_data($this->getTestPrefix().'resourcepage_placeholderitms',array(
            array('placeholderitemid','baseitemid'),
            array(100,1),
            array(100,2),
            array(101,3)
            ),$db);
            
        $added100=$this->module->get_placeholder_added_items(100);
        sort($added100);
        $this->assertEqual($added100,array(1,2));
        $added101=$this->module->get_placeholder_added_items(101);
        sort($added101);
        $this->assertEqual($added101,array(3));
        $plus=$this->module->get_placeholder_added_items_plus(100,$this->getTestPrefix().'resourcepage_items','id');
        $this->assertEqual(count($plus),2);
        $this->assertEqual($plus[1]->title,'test 1');
        $this->assertEqual($plus[2]->title,'test 2');
    }
    
    function test_get_placeholders() {
        global $db;
        make_test_tables_like_real_one(array(
            'resourcepage_items','resourcepage','resourcepage_containeritems'),
            $this->beforeprefix,$this->getTestPrefix(),$db,true);
        load_test_data($this->getTestPrefix().'resourcepage',array(
            array('course','containerid','deletedcontainerid'),
            array(10,101,201),
            array(20,301,401)
            ),$db);
        load_test_data($this->getTestPrefix().'resourcepage_items',array(
            array('id','placeholder'),
            array(1,'thing'),
            array(2,'thing'),
            array(3,'thing'),
            array(4,null), // Not a placeholder
            array(5,'thing'),
            array(6,'thing'),
            array(7,'else')
            ),$db);
        load_test_data($this->getTestPrefix().'resourcepage_containeritems',array(
            array('containerid','subcontainerid','itemid'),
            array(101,null,1), // Item 1 directly in container
            array(201,null,2), // Item 2 directly in deleted container
            array(301,null,3), // Item 3 in other course
            array(101,null,4), // Item 4 in container [but not ph]
            array(101,102,null), // Subcontainer
            array(102,null,5), // One level nested
            array(102,103,null), // Subcontainer
            array(103,null,6), // Two levels nested            
            array(301,null,7) // Item 7 [other ph] in other course
            ),$db);
            
        $this->assert_ph_results($this->module->get_placeholders('thing'),
            array(1=>10,2=>10,3=>20,5=>10,6=>10));
        $this->assert_ph_results($this->module->get_placeholders('thing',10),
            array(1=>10,2=>10,5=>10,6=>10));
        $this->assert_ph_results($this->module->get_placeholders('thing',20),
            array(3=>20));
        $this->assert_ph_results($this->module->get_placeholders('else',20),
            array(7=>20));
    }
    
    private function assert_ph_results($actual,$expected) {
        $resultarray=array();
        foreach($actual as $obj) {
            $resultarray[$obj->placeholderitemid]=$obj->courseid;
        }
        $this->assertEqual(count($resultarray),count($actual));
        ksort($resultarray);
        ksort($expected);
        $this->assertEqual($resultarray,$expected);
    }
}
?>
